/************************************************************************/
/*    Author: Scott Bevin                                               */
/************************************************************************/

#include "PostOffice.h"

#include "Messenger.h"
#include "../XSystemComponents/XDebugging/DebugVariableTracker.h"
#include <algorithm>
#include "../XUtilities/Utilities.h"
#include "../XUtilities/Logger.h"

namespace XF
{
	PostOffice::PostOffice(Application *ownerApp) : SystemComponent(ownerApp)
	{
		Messenger::PostOffice(this);
	}

	PostOffice::~PostOffice()
	{
	}

	bool PostOffice::SubscribeToMailingList( MailingList::ENUM mlist, Messenger *messenger ) //chris: I modified this function to have error checking
	{
		std::string aka = "";
		std::string agentString =  XF::Utilities::ToString(messenger->ID());

		if(LookUpPhoneBookName(messenger->ID(),aka))
		{
			agentString+=" AkA: " +aka+" ";
		}

		if(find(this->mMailingListSubscriptions[mlist].begin(), this->mMailingListSubscriptions[mlist].end(),messenger->ID() ) == this->mMailingListSubscriptions[mlist].end())
		{
			this->mMailingListSubscriptions[mlist].push_back(messenger->ID());
			LOG << "[XF::PostOffice] - Agent: " << agentString <<  "subscribed to mailing list: " << MailingListToString(mlist);
			return true;
		}
		else
		{
			ILOG << "NON fatal Error -[XF::PostOffice] - Agent: " <<agentString << " ALREADY subscribed to mailing list: " << MailingListToString(mlist);
			return false;
		}
	}

	void PostOffice::UnsubscribeFromMailingList( MailingList::ENUM mList, Messenger *messenger )
	{
		if(mMailingListSubscriptions.find(mList) != mMailingListSubscriptions.end())
		{
			std::list<int>::iterator it = std::find(
				mMailingListSubscriptions[mList].begin(),
				mMailingListSubscriptions[mList].end(),
				messenger->ID());

			if(it != mMailingListSubscriptions[mList].end())
				mMailingListSubscriptions[mList].erase(it);

			LOG << "[XF::PostOffice] - Agent: " << messenger->ID() << " unsubscribed to mailing list: " << (int)mList;
		}
	}

	void PostOffice::PostAMessage( int receiverID, int senderID, MessageSubject::ENUM subject, void* data, float sendDelay )
	{
		Message message;

		message.ReceiverID = receiverID;
		message.SenderID = senderID;
		message.SendTime = mTime + sendDelay;
		message.Subject = subject;
		message.Data = data;

		if(sendDelay == 0)
			Dispatch(message);
		else
			this->mPendingMessages.insert(message); 
	}

	void PostOffice::PostAMessage( MailingList::ENUM mailingList, int senderID, MessageSubject::ENUM subject, void* data, float sendDelay )
	{
		if(mMailingListSubscriptions.find(mailingList) != mMailingListSubscriptions.end())
		{
			for(
				std::list<int>::iterator it = this->mMailingListSubscriptions[mailingList].begin();
				it != mMailingListSubscriptions[mailingList].end();
				++it)
				{
					int id = (*it);

					PostAMessage(id, senderID, subject, data, sendDelay);
				}
		}
	}

	void PostOffice::RegisterMessenger( Messenger *messenger )
	{
		mAllMessengers.insert(std::make_pair(messenger->ID(), messenger));
	}

	void PostOffice::UnregisterMessenger( Messenger *messenger )
	{
		mAllMessengers.erase(messenger->ID());
	}

	bool PostOffice::OnInitialise()
	{
		mTime = 0;
		
		return true;
	}

	void PostOffice::OnShutDown()
	{
		Messenger::PostOffice(NULL);
	}

	void PostOffice::OnUpdate( double dT )
	{
		mTime += dT;

		while (mPendingMessages.begin()!= mPendingMessages.end() &&
			mPendingMessages.begin()->SendTime < mTime &&
			mPendingMessages.begin()->SendTime > 0)
		{
			Message message = *mPendingMessages.begin();

			Dispatch(message);
	
			mPendingMessages.erase(mPendingMessages.begin());
		}
	}

	void PostOffice::Dispatch(const  Message &message ) //this could be a bool?
	{
		std::map<int, Messenger*>::iterator it = this->mAllMessengers.find(message.ReceiverID);

		if(it != mAllMessengers.end())
		{
			
			if(it->second->HandleMessage(message)) 
			{
				LOG << "Agent: " << message.ReceiverID << " handled message: " << MessageSubjectToString(message.Subject);
			}
			else
			{
				ILOG << "Agent: " << message.ReceiverID << " failed to handle message: " << MessageSubjectToString(message.Subject);
			}
		}
	}

	void PostOffice::RegisterInPhonebook( std::string name, int id )
	{
		mPhonebook[name] = id;
		LOG << "[XF::PostOffice] - Agent: " << id << " registered in phonebook as: " << name;
	}

	void PostOffice::UnregisterFromPhonebook( std::string name)
	{
		mPhonebook.erase(name);
		LOG << "[XF::PostOffice] - " << name << " unregistered from phonebook";
	}

	int PostOffice::LookupPhonebook( std::string name )
	{
		return mPhonebook[name];
	}

	bool PostOffice::LookupPhonebookSafe( std::string name, int & id )
	{
		PhonebookMap::iterator it = mPhonebook.find(name);
		if(it == mPhonebook.end()) return false;
		
		id = mPhonebook[name];
		return true;
	}
	bool PostOffice::LookUpPhoneBookName(int id, std::string& name)
	{
		PhonebookMap::iterator it;
		for(it = mPhonebook.begin(); it != mPhonebook.end(); ++it)
		{
			if(it->second == id)
			{
				name = it->first;
				return true;
			}
		}
		return false;
	}
}